package com.cooldatasoft.servlet;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.text.Format;
import java.text.Normalizer;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.fop.apps.FOPException;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
import org.apache.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.xml.sax.SAXException;

import com.cooldatasoft.domain.model.Document;
import com.cooldatasoft.domain.model.DocumentEntry;
import com.cooldatasoft.domain.model.DocumentType;
import com.cooldatasoft.domain.model.Project;
import com.cooldatasoft.domain.model.User;
import com.cooldatasoft.domain.model.UserProjectRoles;
import com.cooldatasoft.domain.service.DocumentEntryService;
import com.cooldatasoft.domain.service.DocumentService;
import com.cooldatasoft.domain.service.ProjectsService;
import com.cooldatasoft.domain.service.UserProjectRolesService;
import com.cooldatasoft.domain.service.UserService;
import com.cooldatasoft.security.TaskManagerSecurityManager;
import com.cooldatasoft.taskManager.Utils;

public class ExportPdfServlet extends HttpServlet {

	private static final long serialVersionUID = 1291831176311519895L;
	private ServletContext context;
	private static final Logger logger = Logger.getLogger(ExportPdfServlet.class);
	private TaskManagerSecurityManager securityManager;
	private ProjectsService projectService;
	private UserProjectRolesService userProjectService;
	private DocumentService documentService;
	private DocumentEntryService documentEntryService;
	private UserService userService;
	private String userName;
	private Project currentProject;
	private DocumentType currentType;

	@Override
	public void init(ServletConfig config) throws ServletException {

		context = config.getServletContext();

		ApplicationContext appContext = WebApplicationContextUtils.getRequiredWebApplicationContext(context);

		securityManager = (TaskManagerSecurityManager) appContext.getBean("securityManager");
		projectService = (ProjectsService) appContext.getBean("projectsService");
		userProjectService = (UserProjectRolesService) appContext.getBean("userProjectRolesService");	
		documentService = (DocumentService) appContext.getBean("documentService");	
		documentEntryService = (DocumentEntryService) appContext.getBean("documentEntryService");	
		userService = (UserService) appContext.getBean("userService");	
		
		User currentUser = securityManager.getCurrentUser();
		userName = currentUser.getName() +" " + currentUser.getSurname();		

	}

	private void processRequest(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		logger.debug("processRequest(userRoleMapping.entrySet())");
		
		String projectId = req.getParameter("pid");
		String projectTypeId = req.getParameter("ptid");
		currentProject = projectService.getProjectsByIdProject(Long.parseLong(projectId));
		if(projectTypeId.equals("1")) {
			currentType = DocumentType.USER_GUIDE;
		} else {
			currentType = DocumentType.SYSTEM_GUIDE;
		}
		
		Document doc = documentService.getByProjectAndType(currentProject, DocumentType.MAIN);
		
		List<UserProjectRoles> userProjectRoles = userProjectService.getUsersOnProject(currentProject);
		
		Hashtable<User, String> userRoleMapping = new Hashtable<User, String>();

		for(UserProjectRoles upr : userProjectRoles) {
			if (!userRoleMapping.containsKey(upr.getIdUser())) {
				userRoleMapping.put(upr.getIdUser(), translateRole(upr.getIdRole().getRoleName()));
			} else {
				userRoleMapping.put(upr.getIdUser(),  userRoleMapping.get(upr.getIdUser()) +  ", " + translateRole(upr.getIdRole().getRoleName()));
			}
		}

		try {
			Format formatter = new SimpleDateFormat("dd.MM.yyyy");
			String xsltFile = "documentation.xsl";
			File xsltfile = new File(context.getRealPath("/reports/" + xsltFile));

			// Setup output
			StringBuffer xml = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");

			xml.append("<documentation>");
			xml.append("<data>");
			xml.append("<titlePage>");
			if(currentType.equals(DocumentType.SYSTEM_GUIDE)) {
				xml.append("<docType><![CDATA[Systémová príručka]]></docType>");
			} else {
				xml.append("<docType><![CDATA[Používateľská príručka]]></docType>");
			}
			xml.append("<university><![CDATA[" + doc.getUniversity() + "]]></university>");
			xml.append("<faculty><![CDATA[" + doc.getFaculty() + "]]></faculty>");
			xml.append("<department><![CDATA[" + doc.getDepartment() + "]]></department>");
			xml.append("<lecturer><![CDATA[" + doc.getLecturer() + "]]></lecturer>");
			xml.append("<instructor><![CDATA[" + doc.getInstructor() + "]]></instructor>");
			xml.append("<studyYear><![CDATA[" + doc.getYear() + "]]></studyYear>");
			xml.append("<projectName><![CDATA[" + currentProject.getName() + "]]></projectName>");
			xml.append("<projectDate><![CDATA[" + formatter.format(new Date()) + "]]></projectDate>");
			xml.append("<projectUsers>");
			
			int index = 1;
			for(java.util.Map.Entry<User, String> set : userRoleMapping.entrySet()) {				
				xml.append("<user index=\"" + index++ + "\" name=\"" + set.getKey().getName() + " " + set.getKey().getSurname() + "\" role=\"" + set.getValue() + "\" percentage=\""+ userService.getTaskPercentage(set.getKey(), currentProject) + "%\"></user>");
			}
			xml.append("</projectUsers>");
			xml.append("</titlePage>");	
			xml.append(createContent(currentProject, currentType));
			xml.append("</data>");
			xml.append("</documentation>");

			// configure fopFactory as desired
			FopFactory fopFactory = FopFactory.newInstance();

			FOUserAgent foUserAgent = fopFactory.newFOUserAgent();

			resp.setCharacterEncoding("UTF-8");
			resp.setHeader("Cache-Control", "no-cache");
			resp.setContentType("application/pdf");
			String fileName = "Dokumentacia_" + deAccent(userName.replace(" ", "-")) + ".pdf";
			resp.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");

			fopFactory.setUserConfig(new File(context.getRealPath("/reports/fop-config.xml")));
			fopFactory.getFontManager().setFontBaseURL(context.getRealPath("/reports/"));

			// Construct fop with desired output format
			Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, resp.getOutputStream());

			// Setup XSLT
			TransformerFactory factory = TransformerFactory.newInstance();
			Transformer transformer = factory.newTransformer(new StreamSource(xsltfile));

			// Set the value of a <param> in the stylesheet
			transformer.setParameter("versionParam", "2.0");

			// Setup input for XSLT transformation
			Source src = new StreamSource(new StringReader(xml.toString()));

			// Resulting SAX events (the generated FO) must be piped through to FOP
			Result res = new SAXResult(fop.getDefaultHandler());

			// Start XSLT transformation and FOP processing
			transformer.transform(src, res);
		} catch (TransformerConfigurationException e) {
			logger.error("error during export generate", e);
		} catch (FOPException e) {
			logger.error("error during export generate", e);
		} catch (TransformerException e) {
			logger.error("error during export generate", e);
		} catch (SAXException e) {
			logger.error("error during export generate", e);
		}

	}

	@Override
	public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}

	@Override
	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		processRequest(request, response);
	}

	public String deAccent(String str) {
	    String nfdNormalizedString = Normalizer.normalize(str, Normalizer.Form.NFD);
	    Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
	    return pattern.matcher(nfdNormalizedString).replaceAll("");
	}
	
	private String createContent(Project currentProject_in, DocumentType type_in) {
		StringBuffer sections = new StringBuffer();

		sections.append("<content>");
		
		Document document = documentService.getByProjectAndType(currentProject_in, type_in);
		List<DocumentEntry> entries = documentEntryService.getByDocument(document);
		
		int level = 0;
		for (DocumentEntry entry : entries) {	
			// sections is the tree root, which is not serialized to xml
			if (entry.getParentEntry() == null) {
				
				List<DocumentEntry> mainSections = entry.getDocumentEntries();
				Collections.sort(mainSections);
				for (DocumentEntry section : mainSections) {
					sections.append(createSectionsRecursively(section, level));
				}
				break;
			}
		}
		
		sections.append("</content>");
		
		return sections.toString();
	}
	
	private String createSectionsRecursively(DocumentEntry section_in, int level_in) {
		StringBuffer sections = new StringBuffer();

		String sectionLocalNameStart = determineSectionName(level_in, false);
		String sectionLocalNamEnd = determineSectionName(level_in, true);
		
		sections.append(sectionLocalNameStart);
		sections.append(String.format("<index>%s</index>", section_in.getIndexForDocumentation()));
		sections.append(String.format("<header>%s</header>", section_in.getHeader()));
		String newLineSep = System.getProperty("line.separator");
		if(newLineSep == null) {
			newLineSep = "\n";
		}
		section_in.setText(Utils.stripTags(section_in.getText().replace(newLineSep, "").replace("&nbsp;", " ").replaceAll("<br([^>]*)>", "<br$1 />").replaceAll("<hr([^<]*)>", "<hr$1 />").replaceAll("<img([^<]*)>", "<img$1 />"),"b,strong,a,em,u,span,ul,li,ol,table,h1,h2,h3,h4,h5,h6,p,br,hr,img,th,tr,td,i,"));
		sections.append(String.format("<sectionContent>%s</sectionContent>", section_in.getText()));
		
		
		List<DocumentEntry> subSections = section_in.getDocumentEntries();
		Collections.sort(subSections);
		
		level_in++;
		for (DocumentEntry section : subSections) {	
			sections.append(createSectionsRecursively(section, level_in));
		}
		
        sections.append(sectionLocalNamEnd);
		
		return sections.toString();
	}
	
	private String determineSectionName(int level_in, Boolean isEndTag_in) {
		StringBuffer xmlElementLocalName = new StringBuffer();

		xmlElementLocalName.append(isEndTag_in ? "</" : "<");
		
		for (int index = 0; index < level_in; index++) {
			xmlElementLocalName.append(index == 0 ? "sub" : "Sub");
		}

        xmlElementLocalName.append(level_in == 0 ? "section>" : "Section>");
		
		return xmlElementLocalName.toString();
	}
	
	private String translateRole(String roleName) {
		if(roleName.equalsIgnoreCase("Manager")) {
			return "Manažér";
		} else {
			return "Programátor";
		}
	}
}

